package com.spring.framwork.springmvc;

import com.alibaba.fastjson2.JSONObject;
import com.spring.framwork.spring.annotation.BeanPostProcessor;
import com.spring.framwork.spring.annotation.Component;
import com.spring.framwork.springmvc.annotation.Controller;
import com.spring.framwork.springmvc.annotation.Param;
import com.spring.framwork.springmvc.annotation.RequestMappering;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Component
public class DispatcherServlet extends HttpServlet implements BeanPostProcessor {
    private static final Pattern PATTERN=Pattern.compile("shengsheng\\{.*?}");
    Map<String,WebHandler> handlerMap=new HashMap<>();
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        WebHandler handle = findHandle(req);
        if (handle==null){
            resp.setContentType("text/html;charset=UTF-8");
            resp.getWriter().write("<h1> 你的界面没有对应的处理器</h1> <br>");
            return;
        }
        try {
            Object controllerBean = handle.getControllerBean();
            Method method = handle.getMethod();
            Object[] args=resovleArgs(req,method);
            Object result = method.invoke(controllerBean,args);
            switch (handle.getResultType()){
                case JSON -> {
                    resp.setContentType("application/json;charset=UTF-8");
                    resp.getWriter().write(JSONObject.toJSONString(result));
                }
                case HTML -> {
                    resp.setContentType("text/html;charset=UTF-8");
                    resp.getWriter().write(result.toString());
                }
                case LOCAL -> {
                    ModelAndView resultModeAndView = (ModelAndView) result;
                    String view = resultModeAndView.getView();
                    InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(view);
                    try(resourceAsStream) {
                        String html = new String(resourceAsStream.readAllBytes());
                        html=renderTemplate(html,resultModeAndView.getContext());
                        resp.setContentType("text/html;charset=UTF-8");
                        resp.getWriter().write(html);

                    }
                }
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new ServletException(e);
        }
    }

    private String renderTemplate(String html, Map<String, String> context) {
        Matcher matcher=PATTERN.matcher(html);
        StringBuilder sb=new StringBuilder();
        while (matcher.find()){
            String key=matcher.group(1);
            String value=context.getOrDefault(key,"");
            matcher.appendReplacement(sb,Matcher.quoteReplacement(value));
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    private Object[] resovleArgs(HttpServletRequest req, Method method) {
        Parameter[] parameters = method.getParameters();
        Object[] args=new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            String value=null;
            Param annotation = parameter.getAnnotation(Param.class);
            if (annotation!=null){
                value=req.getParameter(annotation.value());
            }else {
                value=req.getParameter(parameter.getName());
            }
            Class<?> type = parameter.getType();
            if (String.class.isAssignableFrom(type)){
                args[i]=value;
            }else if (Integer.class.isAssignableFrom(type)){
                args[i]=Integer.parseInt(value);
            }else {
                args[i]=null;
            }
        }
        return args;
    }

    /**
     * 通过request找到Handler
     * @param req
     */
    private WebHandler findHandle(HttpServletRequest req) {
        return handlerMap.get(req.getRequestURI());
    }

    @Override
    public Object afterInitializeBean(Object bean, String beanName) {
        if (!bean.getClass().isAnnotationPresent(Controller.class)) {
            return bean;
        }
        RequestMappering classrm = bean.getClass().getDeclaredAnnotation(RequestMappering.class);
        String classURL=classrm!=null? classrm.value():"";
        Arrays.stream(bean.getClass().getDeclaredMethods())
                .filter(m->m.isAnnotationPresent(RequestMappering.class))
                .forEach(m->{
                    RequestMappering methrm=m.getAnnotation(RequestMappering.class);
                    String key=classURL.concat(methrm.value());
                    WebHandler webHandler = new WebHandler(bean, m);
                    if (handlerMap.put(key,webHandler)!=null) {
                        throw new RuntimeException("controller定义重复");
                    }
                });
        return bean;
    }
}
